#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
课程表管理工具（轻量版）
功能：
1. 解析简单格式的课程表文本文件
2. 按教学楼-教室整理课程
3. 生成每日提醒文本
4. 导出为iCalendar格式，可导入手机日历
"""

import os
import sys
import csv
import json
from datetime import datetime, timedelta
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import re

# 常量定义
WEEKDAYS = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
TIME_SLOTS = {
    '1-2': {'start': (8, 0), 'end': (9, 40)},
    '3-4': {'start': (10, 0), 'end': (11, 40)},
    '5-6': {'start': (14, 0), 'end': (15, 40)},
    '7-8': {'start': (16, 0), 'end': (17, 40)},
    '9-10': {'start': (19, 0), 'end': (20, 40)}
}

class CourseScheduler:
    def __init__(self):
        self.courses = []
        self.file_path = None
        self.current_date = datetime.now()
    
    def load_text_file(self, file_path):
        """从文本文件加载课程表数据"""
        try:
            self.file_path = file_path
            courses = []
            
            # 根据文件扩展名选择不同的解析方法
            ext = os.path.splitext(file_path)[1].lower()
            
            if ext == '.txt':
                # 解析文本文件，每行格式：课程名,周一1-2节|地点|教师,周二3-4节|地点|教师,...
                with open(file_path, 'r', encoding='utf-8') as f:
                    lines = f.readlines()
                    for line in lines:
                        line = line.strip()
                        if not line or line.startswith('#'):
                            continue
                        
                        parts = line.split(',')
                        if len(parts) < 2:
                            continue
                        
                        course_name = parts[0].strip()
                        # 处理每周的课程安排
                        for i, weekday_schedule in enumerate(parts[1:], 1):
                            if i > len(WEEKDAYS):
                                break
                            
                            weekday_schedule = weekday_schedule.strip()
                            if not weekday_schedule:
                                continue
                            
                            # 解析单元格内容
                            if '|' in weekday_schedule:
                                schedule_parts = weekday_schedule.split('|')
                                time_info = schedule_parts[0]
                                location = schedule_parts[1] if len(schedule_parts) > 1 else '未知'
                                teacher = schedule_parts[2] if len(schedule_parts) > 2 else '未知'
                            else:
                                time_info = weekday_schedule
                                location = '未知'
                                teacher = '未知'
                            
                            # 提取节次
                            for slot in TIME_SLOTS.keys():
                                if slot in time_info:
                                    course = {
                                        'name': course_name,
                                        'weekday': WEEKDAYS[i-1],
                                        'time_slot': slot,
                                        'location': location,
                                        'teacher': teacher
                                    }
                                    courses.append(course)
                                    break
            
            elif ext == '.csv':
                # 解析CSV文件
                with open(file_path, 'r', encoding='utf-8', newline='') as f:
                    reader = csv.reader(f)
                    headers = next(reader, None)  # 获取表头
                    
                    for row in reader:
                        if not row or not row[0]:
                            continue
                        
                        course_name = row[0].strip()
                        # 处理每周的课程安排
                        for i, weekday_schedule in enumerate(row[1:], 1):
                            if i > len(WEEKDAYS):
                                break
                            
                            weekday_schedule = weekday_schedule.strip()
                            if not weekday_schedule:
                                continue
                            
                            # 解析单元格内容
                            if '|' in weekday_schedule:
                                schedule_parts = weekday_schedule.split('|')
                                time_info = schedule_parts[0]
                                location = schedule_parts[1] if len(schedule_parts) > 1 else '未知'
                                teacher = schedule_parts[2] if len(schedule_parts) > 2 else '未知'
                            else:
                                time_info = weekday_schedule
                                location = '未知'
                                teacher = '未知'
                            
                            # 提取节次
                            for slot in TIME_SLOTS.keys():
                                if slot in time_info:
                                    course = {
                                        'name': course_name,
                                        'weekday': WEEKDAYS[i-1],
                                        'time_slot': slot,
                                        'location': location,
                                        'teacher': teacher
                                    }
                                    courses.append(course)
                                    break
            
            return courses
        except Exception as e:
            raise Exception(f"加载文件失败: {str(e)}")
    
    def parse_course_data(self, courses_data):
        """解析课程数据"""
        self.courses = courses_data
        return self.courses
    
    def organize_by_location(self):
        """按教学楼-教室整理课程"""
        location_dict = {}
        
        for course in self.courses:
            location = course['location']
            # 提取教学楼信息（假设格式为"实验楼B302"，提取"实验楼"）
            building = None
            for kw in ['楼', '馆', '中心']:
                if kw in location:
                    idx = location.find(kw)
                    building = location[:idx+1]
                    break
            
            if building is None:
                building = '其他'
            
            if building not in location_dict:
                location_dict[building] = {}
            
            if location not in location_dict[building]:
                location_dict[building][location] = []
            
            location_dict[building][location].append(course)
        
        return location_dict
    
    def generate_daily_reminder(self, date=None):
        """生成指定日期的课程提醒"""
        if date is None:
            date = self.current_date
        
        weekday = WEEKDAYS[date.weekday()]
        today_courses = [c for c in self.courses if c['weekday'] == weekday]
        
        if not today_courses:
            return f"{date.strftime('%Y年%m月%d日')} {weekday} 今天没有课程，好好休息！"
        
        reminder = f"{date.strftime('%Y年%m月%d日')} {weekday} 课程提醒：\n"
        
        # 按时间顺序排序
        today_courses.sort(key=lambda x: list(TIME_SLOTS.keys()).index(x['time_slot']))
        
        for course in today_courses:
            start_hour, start_minute = TIME_SLOTS[course['time_slot']]['start']
            if start_hour < 12:
                if start_hour <= 8:
                    reminder += f"【早八提醒】{course['name']} - {course['time_slot']}节 - {course['location']} - {course['teacher']}\n"
                else:
                    reminder += f"【上午】{course['name']} - {course['time_slot']}节 - {course['location']} - {course['teacher']}\n"
            else:
                reminder += f"【下午/晚上】{course['name']} - {course['time_slot']}节 - {course['location']} - {course['teacher']}\n"
        
        return reminder
    
    def export_to_ical(self, output_path):
        """导出课程表为iCalendar格式"""
        # 简化版iCalendar生成，不依赖icalendar库
        cal_content = [
            "BEGIN:VCALENDAR",
            "VERSION:2.0",
            "PRODID:-//Course Schedule//course_scheduler.py//",
            "CALSCALE:GREGORIAN"
        ]
        
        # 获取当前学期的开始日期（这里简化处理，假设从当前周的周一开始）
        today = datetime.now()
        start_of_week = today - timedelta(days=today.weekday())
        
        for course in self.courses:
            # 创建事件
            summary = course['name'].replace(';', '\\;')
            description = f"教师：{course['teacher']}\n地点：{course['location']}".replace(';', '\\;')
            location = course['location'].replace(';', '\\;')
            
            # 计算事件的日期和时间
            weekday_idx = WEEKDAYS.index(course['weekday'])
            event_date = start_of_week + timedelta(days=weekday_idx)
            
            start_hour, start_minute = TIME_SLOTS[course['time_slot']]['start']
            end_hour, end_minute = TIME_SLOTS[course['time_slot']]['end']
            
            start_time = event_date.replace(hour=start_hour, minute=start_minute)
            end_time = event_date.replace(hour=end_hour, minute=end_minute)
            
            # 格式化时间为iCalendar格式
            dtstart = start_time.strftime("%Y%m%dT%H%M%S")
            dtend = end_time.strftime("%Y%m%dT%H%M%S")
            uid = f"{course['name']}-{course['weekday']}-{course['time_slot']}@course_scheduler"
            
            # 添加事件组件
            cal_content.extend([
                "BEGIN:VEVENT",
                f"SUMMARY:{summary}",
                f"DESCRIPTION:{description}",
                f"LOCATION:{location}",
                f"DTSTART:{dtstart}",
                f"DTEND:{dtend}",
                "RRULE:FREQ=WEEKLY",
                f"UID:{uid}",
                "END:VEVENT"
            ])
        
        cal_content.append("END:VCALENDAR")
        
        # 保存到文件
        try:
            with open(output_path, 'w', encoding='utf-8') as f:
                f.write('\r\n'.join(cal_content))
            return True
        except Exception as e:
            raise Exception(f"导出iCalendar文件失败: {str(e)}")
    
    def export_location_summary(self, output_path):
        """导出按地点整理的课程摘要"""
        location_dict = self.organize_by_location()
        
        try:
            with open(output_path, 'w', encoding='utf-8') as f:
                f.write("按教学楼-教室整理的课程表\n")
                f.write("="*50 + "\n\n")
                
                for building in sorted(location_dict.keys()):
                    f.write(f"【{building}】\n")
                    f.write("-"*20 + "\n")
                    
                    for location in sorted(location_dict[building].keys()):
                        courses = location_dict[building][location]
                        f.write(f"  {location}:\n")
                        
                        # 按星期和时间排序
                        courses.sort(key=lambda x: (WEEKDAYS.index(x['weekday']), list(TIME_SLOTS.keys()).index(x['time_slot'])))
                        
                        for course in courses:
                            f.write(f"    {course['weekday']} {course['time_slot']}节: {course['name']} ({course['teacher']})\n")
                        
                        f.write("\n")
            
            return True
        except Exception as e:
            raise Exception(f"导出地点摘要文件失败: {str(e)}")

class CourseSchedulerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("课程表管理工具（轻量版）")
        self.root.geometry("800x600")
        self.root.resizable(True, True)
        
        self.scheduler = CourseScheduler()
        
        # 创建界面
        self.create_widgets()
    
    def create_widgets(self):
        """创建GUI组件"""
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 文件选择区域
        file_frame = ttk.LabelFrame(main_frame, text="文件选择", padding="10")
        file_frame.pack(fill=tk.X, pady=10)
        
        self.file_path_var = tk.StringVar()
        file_entry = ttk.Entry(file_frame, textvariable=self.file_path_var, width=60)
        file_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        
        browse_btn = ttk.Button(file_frame, text="浏览...", command=self.browse_file)
        browse_btn.pack(side=tk.LEFT, padx=5)
        
        load_btn = ttk.Button(file_frame, text="加载课程表", command=self.load_course_schedule)
        load_btn.pack(side=tk.LEFT, padx=5)
        
        # 功能区域
        function_frame = ttk.LabelFrame(main_frame, text="功能", padding="10")
        function_frame.pack(fill=tk.X, pady=10)
        
        gen_reminder_btn = ttk.Button(function_frame, text="生成今日课程提醒", command=self.generate_today_reminder)
        gen_reminder_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        export_cal_btn = ttk.Button(function_frame, text="导出为日历文件", command=self.export_calendar)
        export_cal_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        export_location_btn = ttk.Button(function_frame, text="导出按地点整理的课程", command=self.export_location_courses)
        export_location_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        create_sample_btn = ttk.Button(function_frame, text="创建示例课程表", command=self.create_sample_course_table)
        create_sample_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 结果显示区域
        result_frame = ttk.LabelFrame(main_frame, text="结果", padding="10")
        result_frame.pack(fill=tk.BOTH, expand=True, pady=10)
        
        # 创建文本框和滚动条
        self.result_text = tk.Text(result_frame, wrap=tk.WORD, height=20)
        self.result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        scrollbar = ttk.Scrollbar(result_frame, command=self.result_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y, pady=5)
        
        self.result_text.config(yscrollcommand=scrollbar.set)
        
        # 状态区域
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.pack(side=tk.BOTTOM, fill=tk.X)
    
    def browse_file(self):
        """浏览并选择文本文件"""
        file_path = filedialog.askopenfilename(
            title="选择课程表文件",
            filetypes=[("文本文件", "*.txt *.csv"), ("所有文件", "*.*")]
        )
        
        if file_path:
            self.file_path_var.set(file_path)
    
    def load_course_schedule(self):
        """加载并解析课程表"""
        file_path = self.file_path_var.get()
        if not file_path:
            messagebox.showerror("错误", "请先选择课程表文件")
            return
        
        try:
            self.status_var.set("正在加载课程表...")
            self.root.update()
            
            courses_data = self.scheduler.load_text_file(file_path)
            courses = self.scheduler.parse_course_data(courses_data)
            
            self.result_text.delete(1.0, tk.END)
            self.result_text.insert(tk.END, f"成功加载课程表：{os.path.basename(file_path)}\n")
            self.result_text.insert(tk.END, f"共解析到 {len(courses)} 门课程\n\n")
            
            # 显示部分课程信息
            self.result_text.insert(tk.END, "课程列表（前10个）：\n")
            for i, course in enumerate(courses[:10]):
                self.result_text.insert(tk.END, f"{i+1}. {course['name']} - {course['weekday']} {course['time_slot']}节 - {course['location']}\n")
            
            if len(courses) > 10:
                self.result_text.insert(tk.END, f"... 还有 {len(courses) - 10} 门课程\n")
            
            self.status_var.set("课程表加载成功")
        
        except Exception as e:
            messagebox.showerror("错误", str(e))
            self.status_var.set("加载失败")
    
    def generate_today_reminder(self):
        """生成今日课程提醒"""
        if not self.scheduler.courses:
            messagebox.showwarning("警告", "请先加载课程表")
            return
        
        try:
            reminder = self.scheduler.generate_daily_reminder()
            self.result_text.delete(1.0, tk.END)
            self.result_text.insert(tk.END, reminder)
            self.status_var.set("已生成今日课程提醒")
        
        except Exception as e:
            messagebox.showerror("错误", str(e))
            self.status_var.set("生成提醒失败")
    
    def export_calendar(self):
        """导出课程表为iCalendar格式"""
        if not self.scheduler.courses:
            messagebox.showwarning("警告", "请先加载课程表")
            return
        
        # 默认文件名
        default_filename = "课程表.ics"
        if self.scheduler.file_path:
            base_name = os.path.splitext(os.path.basename(self.scheduler.file_path))[0]
            default_filename = f"{base_name}.ics"
        
        # 选择保存路径
        save_path = filedialog.asksaveasfilename(
            title="保存日历文件",
            defaultextension=".ics",
            filetypes=[("iCalendar files", "*.ics"), ("所有文件", "*.*")],
            initialfile=default_filename
        )
        
        if not save_path:
            return
        
        try:
            self.status_var.set("正在导出日历文件...")
            self.root.update()
            
            success = self.scheduler.export_to_ical(save_path)
            if success:
                messagebox.showinfo("成功", f"日历文件已导出到：{save_path}")
                self.status_var.set("日历文件导出成功")
        
        except Exception as e:
            messagebox.showerror("错误", str(e))
            self.status_var.set("导出失败")
    
    def export_location_courses(self):
        """导出按地点整理的课程"""
        if not self.scheduler.courses:
            messagebox.showwarning("警告", "请先加载课程表")
            return
        
        # 默认文件名
        default_filename = "课程地点整理.txt"
        if self.scheduler.file_path:
            base_name = os.path.splitext(os.path.basename(self.scheduler.file_path))[0]
            default_filename = f"{base_name}_地点整理.txt"
        
        # 选择保存路径
        save_path = filedialog.asksaveasfilename(
            title="保存地点整理文件",
            defaultextension=".txt",
            filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")],
            initialfile=default_filename
        )
        
        if not save_path:
            return
        
        try:
            self.status_var.set("正在导出地点整理文件...")
            self.root.update()
            
            success = self.scheduler.export_location_summary(save_path)
            if success:
                messagebox.showinfo("成功", f"地点整理文件已导出到：{save_path}")
                self.status_var.set("地点整理文件导出成功")
        
        except Exception as e:
            messagebox.showerror("错误", str(e))
            self.status_var.set("导出失败")
    
    def create_sample_course_table(self):
        """创建示例课程表文件"""
        # 默认文件名
        default_filename = "示例课程表.txt"
        
        # 选择保存路径
        save_path = filedialog.asksaveasfilename(
            title="保存示例课程表",
            defaultextension=".txt",
            filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")],
            initialfile=default_filename
        )
        
        if not save_path:
            return
        
        try:
            # 创建示例课程表内容
            sample_content = [
                "# 课程表格式：课程名,周一安排,周二安排,周三安排,周四安排,周五安排,周六安排,周日安排",
                "# 每天安排格式：星期X节次|地点|教师，如：周一1-2节|第一教学楼A101|李教授",
                "高等数学,周一1-2节|第一教学楼A101|李教授,,周三3-4节|第一教学楼A101|李教授,,,,",
                "程序设计,,周二3-4节|实验楼B302|王老师,,,周四5-6节|实验楼B302|王老师,,",
                "大学英语,周一5-6节|外语楼C201|张老师,,,,周四1-2节|外语楼C201|张老师,,",
                "物理实验,,,周三9-10节|实验楼D405|刘教授,,,,",
                "毛概,,,,周五3-4节|公共教学楼E101|陈老师,,,"
            ]
            
            with open(save_path, 'w', encoding='utf-8') as f:
                f.write('\n'.join(sample_content))
            
            messagebox.showinfo("成功", f"示例课程表已创建到：{save_path}\n请使用此文件进行测试")
            self.status_var.set("示例课程表创建成功")
            # 自动加载创建的示例文件
            self.file_path_var.set(save_path)
        
        except Exception as e:
            messagebox.showerror("错误", str(e))
            self.status_var.set("创建示例失败")

def main():
    # 检查Python版本
    if sys.version_info < (3, 6):
        print("本程序需要Python 3.6或更高版本")
        sys.exit(1)
    
    # 创建GUI应用
    root = tk.Tk()
    app = CourseSchedulerApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()